using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerGroundedState : PlayerMovementState
{
    private SlopeData slopeData;

    public PlayerGroundedState(PlayerMovementMachine stateMachine) : base(stateMachine)
    {
        slopeData = StateMachine.Player.ColliderUtility.SlopeData;
    }

#region IState Methods
    public override void Enter()
    {
        base.Enter();
        StartAnimation(StateMachine.Player.AnimationData.GroundedParameterHash);
        UpdateSprintState();
    }

    public override void PhysicUpdate()
    {
        base.PhysicUpdate();

        Float();
    }

    public override void Exit()
    {
        base.Exit();
        StopAnimation(StateMachine.Player.AnimationData.GroundedParameterHash);
    }
#endregion

#region Main Methods
    private bool IsThereGroundUnderneath()
    {
        BoxCollider groundCheckCollider = StateMachine.Player.ColliderUtility.TriggerColliderData.GroundCheckCollider;
        var bounds = groundCheckCollider.bounds;
        Vector3 groundCheckCenterInWorldSpace = bounds.center;
        Collider[] overlappedColliders = Physics.OverlapBox(groundCheckCenterInWorldSpace,
            bounds.size,
            groundCheckCollider.transform.rotation,
            StateMachine.Player.PlayerLayerData.GroundLayer,
            QueryTriggerInteraction.Ignore);
        return overlappedColliders.Length > 1;
    }
    
    private void UpdateSprintState()
    {
        if (!StateMachine.ReusableData.ShouldSprint)
            return;

        if (StateMachine.ReusableData.InputMovement != Vector2.zero)
        {
            return;
        }

        StateMachine.ReusableData.ShouldSprint = false;
    }

    private void Float()
    {
        Vector3 capsuleColliderCenterInWorld =
            StateMachine.Player.ColliderUtility.CapsuleColliderData.Collider.bounds.center;
        Ray capsuleCenterToGroundRay = new Ray(capsuleColliderCenterInWorld, Vector3.down);
        if (Physics.Raycast(capsuleCenterToGroundRay, out RaycastHit hit, slopeData.RayToGroundDistance,
                StateMachine.Player.PlayerLayerData.GroundLayer, QueryTriggerInteraction.Ignore))
        {
            float angle = Vector3.Angle(hit.normal, -capsuleCenterToGroundRay.direction);
            float slopeSpeedModify = SetSlopeSpeedModify(angle);
            if (slopeSpeedModify == 0)
                return;

            float floatDistance = StateMachine.Player.ColliderUtility.CapsuleColliderData.ColliderCenterInLocalSpace.y
                * StateMachine.Player.transform.localScale.y - hit.distance;

            if (floatDistance == 0)
                return;

            float liftForce = floatDistance * slopeData.StepReachForce - GetVerticalPlayerVelocity().y;
            StateMachine.Player.Rigidbody.AddForce(new Vector3(0, liftForce, 0), ForceMode.VelocityChange);
        }
    }

    private float SetSlopeSpeedModify(float angle)
    {
        float slopeSpeedModify = slopeData.SlopeAnimationCurve.Evaluate(angle);
        StateMachine.ReusableData.MovementSpeedModifyOnSlope = slopeSpeedModify;
        return slopeSpeedModify;
    }
#endregion

#region Reusable Methods
    protected override void AddInputActionsCallBacks()
    {
        base.AddInputActionsCallBacks();
        StateMachine.Player.PlayerInput.PlayerActions.Movement.canceled += OnMovementCanceled;
        StateMachine.Player.PlayerInput.PlayerActions.Dash.started += OnDashStarted;
        StateMachine.Player.PlayerInput.PlayerActions.Jump.performed += OnJumpPerformed;
    }


    protected override void RemoveInputActionsCallBacks()
    {
        base.RemoveInputActionsCallBacks();
        StateMachine.Player.PlayerInput.PlayerActions.Movement.canceled -= OnMovementCanceled;
        StateMachine.Player.PlayerInput.PlayerActions.Dash.started -= OnDashStarted;
        StateMachine.Player.PlayerInput.PlayerActions.Jump.performed -= OnJumpPerformed;
    }

    protected virtual void OnMove()
    {
        if (StateMachine.ReusableData.ShouldSprint)
        {
            StateMachine.ChangeState(StateMachine.SprintingState);
            return;
        }

        if (StateMachine.ReusableData.ShouldWalk)
        {
            StateMachine.ChangeState(StateMachine.WalkingState);
            return;
        }

        StateMachine.ChangeState(StateMachine.RunningState);
    }

    protected override void OnContactWithGroundExited()
    {
        base.OnContactWithGroundExited();
        
        if(IsThereGroundUnderneath())
            return;
        
        CapsuleColliderData capsuleColliderData = StateMachine.Player.ColliderUtility.CapsuleColliderData;
        Vector3 capsuleColliderCenterInWorld = capsuleColliderData.Collider.bounds.center;
        Ray capsuleBottomToGroundRay =
            new Ray(capsuleColliderCenterInWorld - capsuleColliderData.ColliderVerticalExtents, Vector3.down);

        if (!Physics.Raycast(capsuleBottomToGroundRay, out _, movementData.FallToGroundRayDistance,
                StateMachine.Player.PlayerLayerData.GroundLayer, QueryTriggerInteraction.Ignore))
        {
            OnFall();
        }
    }

    protected virtual void OnFall()
    {
        StateMachine.ChangeState(StateMachine.FallingState);
    }
#endregion

#region Input Methods
    protected virtual void OnMovementCanceled(InputAction.CallbackContext context)
    {
        StateMachine.ChangeState(StateMachine.IdlingState);
    }

    protected virtual void OnJumpPerformed(InputAction.CallbackContext context)
    {
        StateMachine.ChangeState(StateMachine.JumpingState);
    }

    protected virtual void OnDashStarted(InputAction.CallbackContext context)
    {
        StateMachine.ChangeState(StateMachine.DashState);
    }
#endregion
}